/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
**     of its contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtGui>
#include <QtOpenGL>

#include <math.h>

#include "glwidget.h"
#include "dpgmtracking.h"
#include "dcmtk_rt_study.h"
#include "rtss.h"
#include "rtss_roi.h"
#include "rtss_contour.h"

#include "GL/glu.h"

#ifndef GL_MULTISAMPLE
#define GL_MULTISAMPLE  0x809D
#endif

//! [0]
GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(QGLFormat(QGL::SampleBuffers), parent)
{
 //   logo = 0;
    xRot = 0;
    yRot = 0;
    zRot = 0;

    qtGreen = QColor::fromCmykF(0.40, 0.0, 1.0, 0.0);
    qtPurple = QColor::fromCmykF(0.39, 0.39, 0.0, 0.0);

    m_pMain = NULL;
    //m_iMouseWheelDelta = 1;    
    //m_curZoom = 0.5;



    m_dPan[X_AXIS] = 0.0;
    m_dPan[Y_AXIS] = 0.0;
    m_dmMode = DM_NONE;

    m_dLightPosX = 100;
    m_dLightPosY = 100;
    m_dLightPosZ = 100;

    //ZeroMemory(m_texture, sizeof(GLuint)* TEXTURE_CAPACITY);

    m_pmMode = PM_ORTHO;

    m_arrViewBox[0] = -1.0;		//  Viewing volume- "Left"
    m_arrViewBox[1] = 1.0;		//  Viewing volume- "Right"
    m_arrViewBox[2] = -1.0;		//  Viewing volume- "Bottom"
    m_arrViewBox[3] = 1.0;		//  Viewing volume- "Top"
    m_arrViewBox[4] = 1.0;		//  Viewing volume- "Near"
    m_arrViewBox[5] = 1000.0;	//  Viewing volume- "Far"

    m_dZoomFactor = 50.0;
    m_dSphereRadius = 5.0;
    m_dFov = 45.0;
    m_dCameraPosZ = 100.0;

    //m_nTexIndex = 0;
    m_iViewMode = VIEW3D_SAGITTAL;
}
//! [0]

//! [1]
GLWidget::~GLWidget()
{
}
//! [1]
//
////! [2]
//QSize GLWidget::minimumSizeHint() const
//{
//    return QSize(50, 50);
//}
////! [2]
//
////! [3]
//QSize GLWidget::sizeHint() const
////! [3] //! [4]
//{
//    return QSize(400, 400);
//}
////! [4]
//
//static void qNormalizeAngle(int &angle)
//{
//    while (angle < 0)
//        angle += 360 * 16;
//    while (angle > 360 * 16)
//        angle -= 360 * 16;
//}

////! [5]
//void GLWidget::setXRotation(int angle)
//{
//    qNormalizeAngle(angle);
//    if (angle != xRot) {
//        xRot = angle;
//        emit xRotationChanged(angle);
//        updateGL();
//    }
//}
////! [5]
//
//void GLWidget::setYRotation(int angle)
//{
//    qNormalizeAngle(angle);
//    if (angle != yRot) {
//        yRot = angle;
//        emit yRotationChanged(angle);
//        updateGL();
//    }
//}
//
//void GLWidget::setZRotation(int angle)
//{
//    qNormalizeAngle(angle);
//    if (angle != zRot) {
//        zRot = angle;
//        emit zRotationChanged(angle);
//        updateGL();
//    }
//}

//! [6]
void GLWidget::initializeGL()
{
    //qglClearColor(qtPurple.dark());
    qglClearColor(qtPurple.black());

    //logo = new QtLogo(this, 64);
    //logo->setColor(qtGreen.dark());

   // glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glShadeModel(GL_SMOOTH);
   // glEnable(GL_LIGHTING);
   // glEnable(GL_LIGHT0);
    glEnable(GL_MULTISAMPLE);
   // static GLfloat lightPosition[4] = { 0.5, 5.0, 7.0, 1.0 };
   // glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glGetDoublev(GL_MODELVIEW_MATRIX, m_matRotation);		// set rotation matrix to indentity matrix
}
//! [6]

//! [7]
void GLWidget::paintGL()
{
    if (m_pMain == NULL)
        return;

    if (m_pMain->m_pRTstudy == NULL) //if DICOM RT SS is not ready
        return;
    
    glMatrixMode(GL_MODELVIEW);									// Select The Modelview Matrix
    glLoadIdentity();											// Reset The Modelview Matrix
    //SetupLight();			// Setup Light
    SetCamera();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //glLoadIdentity();
    //glTranslatef(0.0, 0.0, -10.0); //should be fixed here
    //glTranslatef(0.0, 0.0, -100.0); //should be fixed here
    /*glRotatef(xRot / 16.0, 1.0, 0.0, 0.0);
    glRotatef(yRot / 16.0, 0.0, 1.0, 0.0);
    glRotatef(zRot / 16.0, 0.0, 0.0, 1.0);*/
    
    //glScalef(0.1, 0.1, 0.1);
    //update zoom
    //glScalef(m_curZoom, m_curZoom, m_curZoom);

//    logo->draw();

    //Draw func here  
    //glEnableClientState(GL_VERTEX_ARRAY);
    //glEnableClientState(GL_NORMAL_ARRAY);

    /*for (int i = 0; i < parts.count(); ++i)
    parts[i]->draw();*/


    //qMultMatrix(mat);

    //original one
    //glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, indices + start)

    Rtss::Pointer rtss = m_pMain->m_pRTstudy->get_rtss();
    int numOfStruct = rtss->num_structures;

    if (m_pMain->m_vStructProperty.size() != numOfStruct)
    {
        cout << "Fatal error! number of structures is not matching" << endl;
        return;
    }
     

    //YKDebug: in future, some flags about "bdraw3D" should come from the main DLG    
    //YKDebug: in future, some color information should come from the main DLG

    
    //This is read only. So mutex would not be neccessary
    VEC3D motionDelta = m_pMain->m_motionDelta;

    /*cout << motionDelta.fX << " " <<
        motionDelta.fY << " " <<
        motionDelta.fZ << endl;*/
    
    STRUCT_PROPT curProperty;

    for (int i = 0; i < numOfStruct; i++)
    {
        curProperty = m_pMain->m_vStructProperty.at(i);

        //bool bSparse = false;
        bool bIncludeMotion = false;
        int zStep = 1;

        if (!curProperty.bVisible3D)
            return;

        Rtss_roi* curRoi = rtss->slist[i];
        //QString ROI_name = curRoi->name.c_str();

        QString ROI_name = curProperty.structName;
        
        //if (ROI_name.contains("Skin"))
        //{
        //    bSparse = true;
        //    zStep = 10;//10 mm
        //}

        zStep = curProperty.iSparsity3D;
        bIncludeMotion = curProperty.bMotionEnable3D;        

        //if (ROI_name.contains("Tumor_1"))
        /* if (ROI_name == strROIName)
         {*/
        //cout << "ROI is found: " << ROI_name.toLocal8Bit().constData() << endl;

        int numContours = curRoi->num_contours;
        //color: 
        //QString strColor =
            //GetColor
         VEC3D fColor;
        //fColor = m_pMain->GetRGBFomDCMStr(curRoi->get_dcm_color_string().c_str());

         fColor = curProperty.fColor;

         //curProperty.Thick is not used now

        glLineWidth(1.0);
        glColor3f(fColor.fX, fColor.fY, fColor.fZ);
               
        for (int j = 0; j < numContours; j += zStep)
        {
            Rtss_contour* curContour = curRoi->pslist[j]; //single contour: contains points with same z value            
           
            if (curContour->num_vertices < 2)
                continue;

            glPushMatrix();
            
            VEC3D curTransformedPt;
            VEC3D nextTransformedPt;
            VEC3D curPosDCM;
            VEC3D nextPosDCM;

            glBegin(GL_LINES);
            for (int k = 0; k < curContour->num_vertices; k++)
            {
                if (k == curContour->num_vertices - 1)
                {
                    curPosDCM.fX = curContour->x[k];
                    curPosDCM.fY = curContour->y[k];
                    curPosDCM.fZ = curContour->z[k];

                    nextPosDCM.fX = curContour->x[0];
                    nextPosDCM.fY = curContour->y[0];
                    nextPosDCM.fZ = curContour->z[0];
                }
                else
                {
                    curPosDCM.fX = curContour->x[k];
                    curPosDCM.fY = curContour->y[k];
                    curPosDCM.fZ = curContour->z[k];

                    nextPosDCM.fX = curContour->x[k + 1];
                    nextPosDCM.fY = curContour->y[k + 1];
                    nextPosDCM.fZ = curContour->z[k + 1];
                    //vOutputPt3D.push_back(tmpPt);

                    //GetTransformedDCM3D(): plan ISo, couch rot, couch shift
                    //transformedPt = GetTransformedDCM3DFromRawDCM(curPosDCM, planIsoDCM, couchTrans, couchRotDeg);                   
                }

                //curTransformedPt = m_pMain->GetTransformedDCM3DFromRawDCM(curPosDCM, bIncludeMotion);
                //nextTransformedPt = m_pMain->GetTransformedDCM3DFromRawDCM(nextPosDCM, bIncludeMotion);

                curTransformedPt = m_pMain->GetTransformedDCM3DFromRawDCM(curPosDCM);
                nextTransformedPt = m_pMain->GetTransformedDCM3DFromRawDCM(nextPosDCM);

                if (bIncludeMotion)
                {
                    curTransformedPt.fX = curTransformedPt.fX + motionDelta.fX;
                    curTransformedPt.fY = curTransformedPt.fY + motionDelta.fY;
                    curTransformedPt.fZ = curTransformedPt.fZ + motionDelta.fZ;

                    nextTransformedPt.fX = nextTransformedPt.fX + motionDelta.fX;
                    nextTransformedPt.fY = nextTransformedPt.fY + motionDelta.fY;
                    nextTransformedPt.fZ = nextTransformedPt.fZ + motionDelta.fZ;
                }

                glVertex3f(curTransformedPt.fX, curTransformedPt.fY, curTransformedPt.fZ);
                glVertex3f(nextTransformedPt.fX, nextTransformedPt.fY, nextTransformedPt.fZ);
            }

            glEnd();
            glPopMatrix();
        }
        //}
    }      
    

   // glDisableClientState(GL_VERTEX_ARRAY);
    //glDisableClientState(GL_NORMAL_ARRAY);
}
//! [7]

//! [8]
void GLWidget::resizeGL(int width, int height)
{
    int side = qMin(width, height);
    glViewport((width - side) / 2, (height - side) / 2, side, side);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
#ifdef QT_OPENGL_ES_1
    glOrthof(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#else
    glOrtho(-0.5, +0.5, -0.5, +0.5, 4.0, 15.0);
#endif
    glMatrixMode(GL_MODELVIEW);
}
//! [8]

//! [9]
void GLWidget::mousePressEvent(QMouseEvent *event)
{
    //lastPos = event->pos();

    //this->x = ev->x();
    //this->y = ev->y();
    
    if (event->button() == Qt::LeftButton)
    {
        m_dmMode = DM_ROTATION;
        m_ptLast = event->pos();

        //emit Mouse_Pressed_Left();
    }
    if (event->button() == Qt::RightButton)
    {
        m_dmMode = DM_PAN;
        m_ptLast = event->pos();

        //emit Mouse_Pressed_Right();
    }
}


void GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        m_dmMode = DM_NONE;
    }
    if (event->button() == Qt::RightButton)
    {
        //emit Mouse_Pressed_Right();
        m_dmMode = DM_NONE;
    }
}

//! [9]

//! [10]
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    /*  int dx = event->x() - lastPos.x();
      int dy = event->y() - lastPos.y();

      if (event->buttons() & Qt::LeftButton) {
      setXRotation(xRot + 8 * dy);
      setYRotation(yRot + 8 * dx);
      } else if (event->buttons() & Qt::RightButton) {
      setXRotation(xRot + 8 * dy);
      setZRotation(zRot + 8 * dx);
      }
      lastPos = event->pos();*/
    QPoint point = event->pos();

    switch (m_dmMode)
    {
    case DM_ROTATION:
    {
                        double diffx, diffy;
                        diffx = (double)(point.x() - m_ptLast.x());
                        diffy = (double)(point.y() - m_ptLast.y());

                        double angleX, angleY;
                        angleX = diffx * 0.5;
                        angleY = diffy * 0.5;

                        //wglMakeCurrent(m_hDC, m_hRC);

                        // make rotation matrix
                        glMatrixMode(GL_MODELVIEW);
                        glLoadIdentity();

                        glRotated(angleX, 0.0, 1.0, 0.0);
                        glRotated(angleY, 1.0, 0.0, 0.0);

                        glMultMatrixd(m_matRotation);
                        glGetDoublev(GL_MODELVIEW_MATRIX, m_matRotation);

                        //wglMakeCurrent(NULL, NULL);
                        updateGL();

                        m_ptLast = point;
                        break;
    }
    case DM_PAN:
    {
                   double diffx, diffy;
                   diffx = (double)(point.x() - m_ptLast.x());
                   diffy = (double)(point.y() - m_ptLast.y());

                   QRect rect;
                   rect = this->rect();
                   m_dPan[X_AXIS] -= diffx / rect.width() * (m_arrViewBox[1] - m_arrViewBox[0]);
                   m_dPan[Y_AXIS] += diffy / rect.height() * (m_arrViewBox[3] - m_arrViewBox[2]);

                   //Invalidate();
                   updateGL();
                   m_ptLast = point;
                   break;
    }
    }

}

void GLWidget::SetMain(DPGMTracking* pMain)
{
    if (pMain != NULL)
        m_pMain = pMain;
}

void GLWidget::wheelEvent(QWheelEvent * event)
{
    if (m_pMain == NULL)
        return;


    emit Mouse_Wheel();

    /*float sensitivity = 10000.0;
    float fMouseWheelDelta = (event->delta() / sensitivity);
    m_curZoom = m_curZoom + fMouseWheelDelta;*/
    /*if (m_curZoom < 0)
        m_curZoom = 0.0001;*/

    short zDelta = event->delta();

    if (zDelta > 0)
        m_dZoomFactor /= 1.1;		// zoom in
    else
        m_dZoomFactor *= 1.1;

    SetProjectionMatrix();
    updateGL();    
}

void GLWidget::SetProjectionMatrix(BOOL bSetIdentity /*= true*/)
{
    glMatrixMode(GL_PROJECTION);											// Select The Projection Matrix
    if (bSetIdentity)
        glLoadIdentity();														// Reset The Projection Matrix

    //CRect rect;
    //GetClientRect(&rect);

    QRect rect = this->rect();
    

    double xextent = double(rect.width()) / (double)min(rect.width(), rect.height());
    double yextent = double(rect.height()) / (double)min(rect.width(), rect.height());

    // calculate the viewing volume
    m_dCameraPosZ = m_dSphereRadius / tan(m_dFov * M_PI / 180.0) + m_dSphereRadius * 2.0; // calculate Z position of camera in perspective mode
    m_dCameraPosZ *= m_dZoomFactor;	// viewing volume was modified by zoom factor. so, we have to apply zoom factor.

    m_arrViewBox[0] = -xextent * m_dSphereRadius * m_dZoomFactor;
    m_arrViewBox[1] = xextent * m_dSphereRadius * m_dZoomFactor;
    m_arrViewBox[2] = -yextent * m_dSphereRadius * m_dZoomFactor;
    m_arrViewBox[3] = yextent * m_dSphereRadius * m_dZoomFactor;
    m_arrViewBox[4] = m_dCameraPosZ - (m_dSphereRadius * 1.5f);
    m_arrViewBox[5] = m_dCameraPosZ + (m_dSphereRadius * 1.5f);

    //if (m_pmMode == PM_PERSPECTIVE)
    gluPerspective(45.0f, (GLfloat)rect.width() / (GLfloat)rect.height(), 0.01, (m_dCameraPosZ + m_dSphereRadius) * 1.2);
        
    //else
        //glOrtho(m_arrViewBox[0], m_arrViewBox[1], m_arrViewBox[2], m_arrViewBox[3], m_arrViewBox[4], m_arrViewBox[5]);
}

void GLWidget::SetCamera()
{
    glTranslated(-m_dPan[X_AXIS], 0, 0);		// panX
    glTranslated(0, -m_dPan[Y_AXIS], 0);		// panY

    glTranslated(0, 0, -m_dCameraPosZ);			// zoom

    glMultMatrixd(m_matRotation);
}

void GLWidget::InitRot()
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    if (m_iViewMode == VIEW3D_SAGITTAL)
    {
        glRotated(-90.0, 1.0, 0.0, 0.0);
    }
/*
    if (m_iViewMode == VIEW3D_FRONTAL)
    {
        glRotated(90.0, 0.0, 1.0, 0.0);
    }
    
    else if (m_iViewMode == VIEW3D_AXIAL)
    {
        glRotated(90.0, 0.0, 0.0, 1.0);
    }
*/
    //glRotated( 90.0, 1.0, 0.0, 0.0 );

    glMultMatrixd(m_matRotation);
    glGetDoublev(GL_MODELVIEW_MATRIX, m_matRotation);
    updateGL();
}


void GLWidget::SetupLight()
{
    GLfloat mat_specular[] = { 0.5, 0.5, 0.5, 1.0 };
    GLfloat mat_shininess[] = { 50.0 };
    GLfloat light_position[] = { (float)m_dLightPosX, (float)m_dLightPosY, (float)m_dLightPosZ, 1.0 };
    GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat wlmodel_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };

    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, wlmodel_ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
}

void GLWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
    // make rotation matrix
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    if (m_iViewMode == VIEW3D_FRONTAL)
    {
        glRotated(90.0, 0.0, 1.0, 0.0);
    }
    else if (m_iViewMode == VIEW3D_SAGITTAL)
    {
        glRotated(90.0, 1.0, 0.0, 0.0);
    }
    else if (m_iViewMode == VIEW3D_AXIAL)
    {
        glRotated(90.0, 0.0, 0.0, 1.0);
    }

    //glRotated( 90.0, 1.0, 0.0, 0.0 );

    glMultMatrixd(m_matRotation);
    glGetDoublev(GL_MODELVIEW_MATRIX, m_matRotation);   

    updateGL();
}
